package com.lixinlei.meetingfilm.cinema.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Maps;
import com.lixinlei.meetingfilm.cinema.controller.vo.CinemaSavedReqVO;
import com.lixinlei.meetingfilm.cinema.controller.vo.DescribeCinemasRespVO;
import com.lixinlei.meetingfilm.cinema.service.CinemaServiceAPI;
import com.lixinlei.meetingfilm.utils.common.vo.BasePageVO;
import com.lixinlei.meetingfilm.utils.common.vo.BaseResponseVO;
import com.lixinlei.meetingfilm.utils.exception.CommonServiceException;
import com.netflix.discovery.converters.Auto;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping("/cinemas")
public class CinemaController {

    @Autowired
    private CinemaServiceAPI cinemaServiceAPI;

    @RequestMapping(value = "/cinema:add", method = RequestMethod.POST)
    public BaseResponseVO saveCinema(@RequestBody CinemaSavedReqVO cinemaSavedReqVO)
            throws CommonServiceException {

        cinemaSavedReqVO.checkParam();
        cinemaServiceAPI.saveCinema(cinemaSavedReqVO);
        return BaseResponseVO.success();
    }


    /**
     * @HystrixCommand 注解相当于把 describeCinemas 方法变成了 Hystrix 的 run() 方法
     */
    @HystrixCommand(
        // 指定降级方法
        fallbackMethod = "fallbackMethod",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
            // 方法的执行时间超过这个值，就会走降级方法 fallbackMethod
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value= "1000"),
            // 单位时间内请求的次数，超过这个值，就开始进入熔断检测机制
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            // 单位时间内请求的次数超过 requestVolumeThreshold 的值，并且错误率达到 50%，就触发熔断，触发熔断，就进入 fallbackMethod 方法
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
        },
        threadPoolProperties = {
            // 线程池超了，也会进入 fallbackMethod 方法，触发业务降级
            @HystrixProperty(name = "coreSize",  value = "1"),
            @HystrixProperty(name = "maxQueueSize", value = "10"),
            @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"),
            @HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"),
            @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
            @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500")
        },
        // 不希望 CommonServiceException 异常的产生而触发降级
        ignoreExceptions = CommonServiceException.class
    )
    @RequestMapping(value = "", method = RequestMethod.GET)
    public BaseResponseVO describeCinemas(BasePageVO basePageVO) throws CommonServiceException {

        IPage<DescribeCinemasRespVO> describeCinemasRespVOIPage
                = cinemaServiceAPI.describeCinemas(basePageVO.getNowPage(), basePageVO.getPageSize());

        if(basePageVO.getNowPage() > 10000){
//            throw new CommonServiceException(400,"nowPage太大了，不支持此处理");
            try {
                // 这就足够超过 execution.isolation.thread.timeoutInMilliseconds 设定的值了
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return BaseResponseVO.success(assemblePageResult("cinemas", describeCinemasRespVOIPage));
    }

    /**
     * fallback 是业务处理的保底方案，尽可能保证这个保底方案一定能成功；
     * 该方法的签名必须是和 describeCinemas 方法的一样，包括异常；
     * describeCinemas 方法执行触发熔断、超出线程池、抛出异常、超时都会进入该方法，触发业务降级；
     */
    public BaseResponseVO fallbackMethod(BasePageVO basePageVO) throws CommonServiceException{
        Map<String, String> result = new HashMap<>();
        result.put("code", "500");
        result.put("message", "请求处理降级返回");

        // describeCinemas -> 获取影院列表 -> 在 Redis 中查询影院列表[redis挂了，或者数据没了] -> 获取超时

        // fallback里 捕获到超时或者数据内容与分页数不一致

        // fallback 就在数据库中查询真实的影院信息，然后返回

        // 返回一定是成功，或者业务处理失败
        return BaseResponseVO.success(result);
    }

    private Map<String, Object> assemblePageResult(String key, IPage page) {
        Map<String, Object> result = Maps.newHashMap();
        result.put("totalSize", page.getTotal());
        result.put("totalPages", page.getPages());
        result.put("pageSize", page.getSize());
        result.put("nowPage", page.getCurrent());
        result.put(key, page.getRecords());
        return result;
    }

}
